#include "Othello.h"
#include "math.h"
#include "ResourceManager.h"
#include "MyVector.h"
#include "FxChessDown.h"
#include "FxFalling.h"
#include "SoundManager.h"

#define BOARD_HEIGHT 8
#define BOARD_WIDTH 8
#define BOARD_UNIT 1
#define CHESS_RADIUS 0.4f

const double PI = atan(1.0)*4;
FxChessDown *fxChessDown = new FxChessDown();
FxFalling *fxFalling = new FxFalling(Vector3(4, 8, 4));

Othello::Othello()
{
#pragma region Default_Value

	m_isEndGame = false;
	m_turn = ETurn::HUMAN;
	m_humanChessCounter = m_computerChessCounter = 0;
	m_selectionPos.X = 3,m_selectionPos.Z = 5;
	m_boardPos.X = m_boardPos.Y = m_boardPos.Z = 0;
	timeCounter=0;

#pragma endregion Default_Value
	
#pragma region Create_Chess_Array

	m_chessArray = new int*[BOARD_HEIGHT];

	for(int i = 0; i < BOARD_HEIGHT; i++) 
		m_chessArray[i] = new int[BOARD_HEIGHT];
	for(int i = 0 ; i < BOARD_HEIGHT; i++)
		for(int j = 0;j < BOARD_HEIGHT; j++)
			m_chessArray[i][j] = 0;

	m_chessArray[3][3] = ETurn::HUMAN;
	m_chessArray[3][4] = ETurn::COMPUTER;
	m_chessArray[4][3] = ETurn::COMPUTER;
	m_chessArray[4][4] = ETurn::HUMAN;

#pragma endregion Create_Chess_Array

}

Othello::~Othello()
{

}

void Othello::Reset()
{
	
#pragma region Reset_Defaul_Value

	fxChessDown->Delete();

	m_isEndGame = false;
	m_turn = ETurn::HUMAN;
	m_humanChessCounter = m_computerChessCounter = 0;
	m_selectionPos.X = 3,m_selectionPos.Z = 5;
	m_boardPos.X = m_boardPos.Y = m_boardPos.Z = 0;
	timeCounter = 0.0f;

#pragma endregion Reset_Defaul_Value
	
#pragma region Reset_Chess_Array

	for(int i = 0 ; i < BOARD_HEIGHT; i++)
		for(int j = 0;j < BOARD_HEIGHT; j++)
			m_chessArray[i][j] = 0;

	m_chessArray[3][3] = ETurn::HUMAN;
	m_chessArray[3][4] = ETurn::COMPUTER;
	m_chessArray[4][3] = ETurn::COMPUTER;
	m_chessArray[4][4] = ETurn::HUMAN;

#pragma endregion Reset_Chess_Array

}

GLvoid Othello::DrawBoard()
{
	
	int i, j;


	glPushMatrix();
	
	glDepthMask(true);
	glEnable(GL_TEXTURE_2D);
	ResourceManager::GetInst()->LoadTexture("Resource/texture/leo.jpg");
	
	glTranslatef(m_boardPos.X, m_boardPos.Y, m_boardPos.Z);

	for(i = 0; i < BOARD_HEIGHT; i += BOARD_UNIT)
	{
		for(j = 0; j < BOARD_WIDTH; j += BOARD_UNIT)
		{
			/// Cho mau xen ke nhau
			if(i % 2 == 0) 
			{
				if(j % 2 == 0) 
					glColor4f(1, 0.38f, 0.27f, 1);
				else 
					glColor4f(1, 0.62f, 0.47f, 1);
			}
			else
			{
				if(j % 2 != 0) 
					glColor4f(1, 0.38f, 0.27f, 1);
				else 
					glColor4f(1, 0.62f, 0.47f, 1);
			}


			glBegin(GL_QUADS);

			glTexCoord2f(1 - (float) i / BOARD_HEIGHT, 1 - (float) j / BOARD_WIDTH);
			glVertex3f(i, 0, j);

			glTexCoord2f(1 - (float) i / BOARD_HEIGHT, 1 - (float) (j + BOARD_UNIT) / BOARD_WIDTH); 
			glVertex3f(i, 0, j + BOARD_UNIT);

			glTexCoord2f(1 - (float) (i + BOARD_UNIT) / BOARD_HEIGHT, 1 - (float) (j + BOARD_UNIT) / BOARD_WIDTH); 
			glVertex3f(i + BOARD_UNIT, 0, j + BOARD_UNIT);

			glTexCoord2f(1 - (float) (i + BOARD_UNIT) / BOARD_HEIGHT, 1 - (float) j / BOARD_WIDTH);
			glVertex3f(i + BOARD_UNIT, 0, j);
			glEnd();
		}
	}
	glDepthMask(false);
	glDisable(GL_TEXTURE_2D);

	glPopMatrix();
}

GLvoid DrawCircle(float r)
{
	glPushMatrix();
	
	glTranslatef(0, 0, 0);

	glBegin(GL_TRIANGLE_FAN);
	glVertex3f(0, 0, 0);
	for(float i = 0; i <= 360; i += 10)
	{
		glVertex3f(r*cos(i*PI/180), 0, r*sin(i * PI / 180));
	}
	glEnd();

	glPopMatrix();
}

GLvoid DrawDepth(float r)
{
	glPushMatrix();
	glTranslatef(0, 0, 0);
	glColor3f( 0, 0.52f, 0.4f);
	glBegin(GL_QUAD_STRIP);
	for(float i = 0; i <= 360; i += 10)
	{
		glColor3f( 0, 0.52f, 0.4f);
		glVertex3f(r * cos(i * PI / 180), 0, r * sin(i * PI / 180));
		glColor3f( 0.86f, 0.07f, 0.3f);
		glVertex3f(r * cos(i * PI / 180), -0.2f, r * sin(i * PI / 180));
	}
	glEnd();
	glPopMatrix();
}

GLvoid Othello::DrawChess(float x,float z,int m_turn)
{
	if(m_turn == ETurn::NONE) 
		return;
	glPushMatrix();

	x += (float) BOARD_UNIT / 2;
	z += (float) BOARD_UNIT / 2;

	glTranslatef(x, 0.01f, z);

	glDepthMask(true);
	
	if(m_turn == ETurn::COMPUTER)
	{
		///Ve mat tren mau hong
		glColor4f( 0, 0.52f, 0.54f, 0.8f);
		DrawCircle(CHESS_RADIUS);
		
		///Ve mat duoi mau xanh
		glTranslatef(0, 0.2f, 0);
		glColor4f( 0.86f, 0.07f, 0.3f, 0.8f);
		DrawCircle(CHESS_RADIUS);
		
		///Ve chieu cao cua co
		glRotatef(180, 0, 0, 1);
		glTranslatef(0, 0.2f, 0);
		DrawDepth(CHESS_RADIUS);
	}
	else
	{
		///Ve mat tren mau xanh
		glColor4f( 0.86f, 0.07f, 0.3f, 0.8f);//mat duoi (hong)
		DrawCircle(CHESS_RADIUS);//ve mau mau hong

		///Ve mat duoi mau hong
		glTranslatef(0, 0.2f, 0);
		glColor4f( 0, 0.52f, 0.54f, 0.8f);//mat tren (xanh)
		DrawCircle(CHESS_RADIUS);//ve mat mau xanh

		///Ve chieu cao cua co
		DrawDepth(CHESS_RADIUS);//ve do day cua quan co
	}
	
	glDepthMask(true);
	glPopMatrix();

}

GLvoid Othello::DrawSelection()
{
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glPushMatrix();
	glTranslatef(m_selectionPos.X, 0.01f, m_selectionPos.Z);
	
	///Neu o do co th dat quan co thi hien thi mau nhu quan cua nguoi dang toi luot
	if(IsMoveValid(m_selectionPos.X, m_selectionPos.Z)) 
	{
		if(m_turn == ETurn::HUMAN) 
			glColor4f( 0, 0.52f, 0.54f, 0.7f);
		if(m_turn == ETurn::COMPUTER) 
			glColor4f(0.86f, 0.07f, 0.23f, 0.7f);
	}
	else glColor4f( 0, 0, 0, 0.7f);

	glBegin(GL_QUADS);

	glVertex3f(0, 0, 0);
	glVertex3f(0, 0, 1);
	glVertex3f(1, 0, 1);
	glVertex3f(1, 0, 0);

	glDisable(GL_BLEND);
	glEnd();
	glPopMatrix();

}

void Othello::ChessMoveLeft()
{
	if(m_selectionPos.X > 0) 
	{
		m_selectionPos.X -= 1;
		if(!HasPoisibleMove()) SwitchPlayer();
	}
}

void Othello::ChessMoveRight()
{
	if(m_selectionPos.X < BOARD_WIDTH - 1) 
	{
		m_selectionPos.X += 1;
		if(!HasPoisibleMove()) SwitchPlayer();
	}
}

void Othello::ChessMoveUp()
{
	if(m_selectionPos.Z > 0) 
	{
		m_selectionPos.Z -= 1;
		if(!HasPoisibleMove()) SwitchPlayer();
	}
}

void Othello::ChessMoveDown()
{
	if(m_selectionPos.Z < BOARD_HEIGHT - 1) 
	{
		m_selectionPos.Z += 1;
		if(!HasPoisibleMove()) SwitchPlayer();
	}
}

void Othello::SwitchPlayer()
{
	m_turn *= -1;
}

void Othello::Update(float _dt)
{
	
	fxFalling->Update(_dt);
	fxChessDown->Update(_dt);

	if(!m_isEndGame)
	{
		timeCounter += _dt;
		if((float) timeCounter / 1000 >= 2) //delay for 2 second
		{
			///Human vs AI
			if(m_turn == COMPUTER) 
			{
				if(HasPoisibleMove()) 
				{
					MinimaxAI();
				}
				else
				{
					SwitchPlayer();
					if(!HasPoisibleMove()) m_isEndGame = true;
				}
				timeCounter = 0;

			}
		}
	}
}

void Othello::Draw(float _posX, float _posY, float _posZ, 
				   float _viewX, float _viewY, float _viewZ)
{
	DrawBoard();
	DrawChessArray();
	if(m_turn == HUMAN)
		DrawSelection();
	fxChessDown->Draw(_posX, _posY, _posZ, 
		_viewX,  _viewY,  _viewZ);
	fxFalling->Draw(_posX, _posY, _posZ, 
		 _viewX,  _viewY,  _viewZ);
}

void Othello::DrawChessArray()
{
	
	m_humanChessCounter = m_computerChessCounter = 0;
	for(int i = 0; i < BOARD_HEIGHT; i++)
		for(int j = 0; j < BOARD_HEIGHT; j++)
		{
			if(m_chessArray[i][j] == ETurn::HUMAN)
			{
				DrawChess(i, j, ETurn::HUMAN);
				m_humanChessCounter++;
			}

			if(m_chessArray[i][j] == ETurn::COMPUTER) 
			{
				DrawChess(i, j, ETurn::COMPUTER);
				m_computerChessCounter++;
			}
		}
		if(m_humanChessCounter + m_computerChessCounter == BOARD_HEIGHT*BOARD_WIDTH) 
			m_isEndGame = true;


}

bool Othello::IsMoveValid(int posX, int posZ)
{
	int m, n;
	if(m_chessArray[posX][posZ] != 0) 
		return false;
	for(int i = posX - 1; i <= posX + 1; i++)
		for(int j = posZ - 1; j <= posZ + 1; j++)
		{
			if(i < BOARD_HEIGHT && j < BOARD_HEIGHT && i >= 0 && j >= 0 )
			{
				if(m_chessArray[i][j] == -m_turn) 
				{
					for(m = i, n = j; ; m += (i - posX), n += (j - posZ))
					{
						if(m >= BOARD_HEIGHT || n >= BOARD_HEIGHT || m < 0 || n < 0 ) 
							break; 
						if(m_chessArray[m][n] == 0) 
							break;
						if(m_chessArray[m][n] == m_turn) 
							return true;
					}
				}
			}
		}
		return false;
}

void Othello::ChessFlip()
{
	SoundManager::GetInst()->PlaySound("Resource/sound/Message.wav");

	bool isFlipDone = false;
	int m, n;
	for(int i = m_selectionPos.X - 1; i <= m_selectionPos.X + 1; i++)
		for(int j = m_selectionPos.Z - 1; j <= m_selectionPos.Z + 1; j++)
		{
			if(i < BOARD_HEIGHT && j < BOARD_HEIGHT && i >= 0 && j >= 0 && m_chessArray[i][j] == -m_turn)
			{
				for(m = i, n = j; ; m += (i - m_selectionPos.X), n += (j - m_selectionPos.Z))
				{
					if(isFlipDone) 
					{
						isFlipDone = false;
						break;
					}
					if(m >= BOARD_HEIGHT || n >= BOARD_HEIGHT || m < 0 || n < 0 || m_chessArray[m][n]==0) 
						break;
					if(m_chessArray[m][n] == m_turn) 
					{
						for(int q = i, p = j ; ; q += (i - m_selectionPos.X), p += (j - m_selectionPos.Z))
						{
							if(q == m && p == n)
							{	
								isFlipDone = true;
								break;
							}

							m_chessArray[q][p] *= -1;
							fxChessDown->AddParticle(q + (float) BOARD_UNIT / 2, 0.3, p + (float)BOARD_UNIT / 2);
						}
					}
				}
			}
		}
}

void Othello::ChessDown()
{
	if(m_chessArray[m_selectionPos.X][m_selectionPos.Z] != 0) 
		return;
	m_chessArray[m_selectionPos.X][m_selectionPos.Z] = m_turn;
	ChessFlip();
	SwitchPlayer();
}

int Othello::NumberOfChessCanFlip(int posX, int posZ)
{
	bool isCountDone = false;
	int m, n, counter = 0;
	for(int i = posX - 1; i <= posX + 1; i++)
		for(int j = posZ - 1; j <= posZ + 1; j++)
		{
			if(i < BOARD_HEIGHT && j < BOARD_HEIGHT && i >= 0 && j >= 0 && m_chessArray[i][j] == -m_turn)
			{	
				for(m = i, n = j; ; m += (i - posX), n += (j - posZ))
				{
					if(isCountDone) 
					{
						isCountDone = false;
						break;
					}
					if(m >= BOARD_HEIGHT || n >= BOARD_HEIGHT || m < 0 || n < 0 || m_chessArray[m][n] == 0) 
						break;
					if(m_chessArray[m][n] == m_turn) 
					{
						for(int q = i, p= j ; ; q += (i - posX), p += (j - posZ))
						{
							if(q == m && p == n)
							{	
								isCountDone = true;
								break;
							}
							if(m_chessArray[q][p] == -m_turn)
								counter++;
						}
					}
				}
			}
		}
		return counter;
}

void Othello::NormalAI()
{

	int i, j;
	int maxFlip = 0;//maxChessCanFlipThisTurn
	CVector chosenMovePos;
	int countFlip = -1; //Number of chesses can flip this move

	for(i = 0; i < BOARD_HEIGHT; i++)
	{
		for(j = 0; j < BOARD_HEIGHT; j++)
		{
			/*m_selectionPos.X = i;
			m_selectionPos.Z = j;*/
			if(IsMoveValid(i, j)) 
			{
				///sua code NormalAI tu day
				countFlip = NumberOfChessCanFlip(i, j);
				if(countFlip > maxFlip)
				{
					chosenMovePos.X = i;
					chosenMovePos.Z = j;
					maxFlip = countFlip;
				}
			}
		}
	}
	if(countFlip == -1) 
		SwitchPlayer();
	else
	{
		m_selectionPos.X = chosenMovePos.X;
		m_selectionPos.Z = chosenMovePos.Z;
		ChessDown();
	}
}

void Othello::MinimaxAI()
{

	int i, j, m, n;
	int maxFlip = 0;
	int minFlip = BOARD_HEIGHT*BOARD_HEIGHT;//minChessCanFlipNextTurn
	CVector chosenMovePos;
	chosenMovePos.X = -1;
	chosenMovePos.Z = -1;
	int countFlip = -1;//chessCanFlipThisMove

	for(i = 0; i < BOARD_HEIGHT; i++)
	{
		for(j = 0; j < BOARD_HEIGHT; j++)
		{
			/*m_selectionPos.X = i;
			m_selectionPos.Z = j;*/
			if(IsMoveValid(i, j)) 
			{
				//i,j la bien luu lai vi tri co
				m_chessArray[i][j] = m_turn;
				SwitchPlayer();
				for(m = 0; m < BOARD_HEIGHT; m++)
				{
					for(n = 0; n < BOARD_HEIGHT; n++)
					{
						/*m_selectionPos.X = m;
						m_selectionPos.Z = n;*/
						if(IsMoveValid(m, n)) 
						{
							countFlip = NumberOfChessCanFlip(m,n);
							if(countFlip > maxFlip)
							{
								maxFlip = countFlip;
							}
						}

					}
				}
				if(maxFlip < minFlip)
				{
					minFlip = maxFlip;
					chosenMovePos.X = i;
					chosenMovePos.Z = j;
				}
				m_chessArray[i][j] = 0;
				SwitchPlayer();
			}
		}
	}
	if(chosenMovePos.X == -1 || chosenMovePos.Z == -1) 
		SwitchPlayer();
	else
	{
		m_selectionPos.X = chosenMovePos.X;
		m_selectionPos.Z = chosenMovePos.Z;
		ChessDown();
	}

}

bool Othello::HasPoisibleMove()
{
	int i, j;
	for(i = 0; i < BOARD_HEIGHT; i++)
	{
		for(j = 0;j < BOARD_WIDTH; j++)
		{
			if(IsMoveValid(i,j)) 
			{
				return true;
			}
		}
	}
	return false;
}


